<!DOCTYPE html>
<!--
Copyright 2017 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->

<link rel="import" href="/tracing/base/raf.html">
<link rel="import" href="/tracing/base/timing.html">
<link rel="import" href="/tracing/value/histogram_set.html">

<script>
'use strict';
tr.exportTo('tr.v', function() {
  class HistogramImporter {
    /**
     * @param {!Element} loadingEl
     */
    constructor(loadingEl) {
      this.loadingEl_ = loadingEl;
      this.histograms_ = undefined;
      this.string_ = '';
      this.dataOffset_ = 0;
      this.view_ = undefined;
      this.fmpMark_ = tr.b.Timing.mark('HistogramImporter', 'fmp');

      this.loadingEl_.textContent = 'Parsing HTML...';
      // The json comment appears after this script tag in results.html, so the
      // browser will parse them into DOM now.
    }

    /**
     * @param {string} message
     * @return {Promise} resolves when |message| is displayed.
     */
    async update_(message) {
      this.loadingEl_.textContent = message;
      // Use rAF callbacks only if the document is visible. If the document is
      // hidden, then the user-agent can stop triggering the rAF callbacks. So
      // avoid rAF callbacks when hidden.
      if (window.document.visibilityState === 'visible') {
        await tr.b.animationFrame();
      }
    }

    /**
     * The string contains a list of histograms of the following form:
     *  JSON\n
     *  JSON\n
     *  ...
     * The |view| should have 'display:none' so that it doesn't obnoxiously
     * display "zero Histograms" while they are being imported.
     *
     * @param {!String[]} strings
     * @param {!Element} view A histogram-set-view.
     * @return {Promise} resolves when |view| is displayed.
     */
    async importHistograms(strings, view) {
      this.histograms_ = new tr.v.HistogramSet();
      this.view_ = view;
      tr.b.Timing.instant('HistogramImporter', 'string', this.string_.length);
      const loadMark = tr.b.Timing.mark('HistogramImporter', 'loadHistograms');
      for (const string of strings) {
        this.string_ = string;
        this.dataOffset_ = 0;
        if (this.string_.length == 0) continue;
        await this.update_('Loading Histogram 0');
        if (!this.findDataStart_()) continue
        await this.loadSomeHistograms_();
      }
      loadMark.end();
      tr.b.Timing.instant('HistogramImporter', 'nsPerJson',
          parseInt(1e3 * loadMark.durationMs / this.histograms_.length));

      await this.update_('Displaying Histogram table...');
      await this.displayHistograms_();
    }

    findDataStart_() {
      // Find the initial data start.
      this.dataOffset_ = this.string_.indexOf('\n', this.dataOffset_);
      if (this.dataOffset_ < 0) return false;
      // Skip over newline character.
      this.dataOffset_++;
      return true;
    }

    async loadSomeHistograms_() {
      // Don't spend so long on this chunk of Histograms that the user gets
      // frustrated, but also don't call requestAnimationFrame faster than every
      // 16ms, so that the browser doesn't have to wait for the next vsync.
      // Powerful computers can load several hundred Histograms in 32ms.
      // Also don't call performance.now() more often than necessary.
      const startTime = performance.now();
      do {
        for (let i = 0; i < 100; i++) {
          const endIndex = this.string_.indexOf('\n', this.dataOffset_);
          if (endIndex < 0) return;
          const json = this.string_.substring(this.dataOffset_, endIndex);
          const dict = JSON.parse(json);
          if (dict instanceof Array) {
            // TODO(benjhayden): progress callback
            this.histograms_.importDicts(dict);
          } else {
            this.histograms_.importLegacyDict(dict);
          }
          // Continue after last found newline character.
          this.dataOffset_ = endIndex + 1;
        }
      } while (performance.now() - startTime < 50);

      await this.update_(`Loading Histogram ${this.histograms_.length}`);
      await this.loadSomeHistograms_();
    }

    async displayHistograms_() {
      this.view_.addEventListener('display-ready', async() => {
        this.loadingEl_.style.display = 'none';
        this.view_.style.display = 'block';
        await tr.b.animationFrame();
        this.fmpMark_.end();
      });

      await this.view_.build(this.histograms_, {
        progress: message => this.update_(message),
        helpHref: 'https://github.com/catapult-project/catapult/blob/master/docs/metrics-results-ui.md',
        feedbackHref: 'https://docs.google.com/a/google.com/forms/d/e/1FAIpQLSfXvMvm_z2F9-khFaKyH_LHVZ6caPPkxI27BZqMnEt4XjyJ3g/viewform',
      });
    }
  }

  return {
    HistogramImporter,
  };
});
</script>
